package com.burningtower.service.impl;

import com.alipay.api.AlipayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.ExtendParams;
import com.alipay.demo.trade.model.GoodsDetail;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.service.impl.AlipayTradeServiceImpl;
import com.alipay.demo.trade.utils.ZxingUtils;
import com.burningtower.common.Pages;
import com.burningtower.common.Result;
import com.burningtower.entity.*;
import com.burningtower.enums.AliCallbackEnum;
import com.burningtower.enums.OrderStatusEnum;
import com.burningtower.repo.OrderRepository;
import com.burningtower.service.*;
import com.burningtower.utils.Taest;
import com.burningtower.vo.OrderVo;
import com.burningtower.vo.ShippingVo;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;

import static com.burningtower.enums.OrderStatusEnum.SUCCESS;

@Service
public class OrderServiceImpl implements OrderService {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);
    @Value("${returnUrl}")
    String returnUrl;
    @Value("${qrUrl}")
    String qrUrl;
    @Value("${qiniuUrl}")
    String qiniuUrl;
    @Autowired
    private OrderRepository repository;
    @Autowired
    private CartService cartService;
    @Autowired
    private ProductService productService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private ShippingService shippingService;
    @Autowired
    private PayInfoService payInfoService;

    @Transactional
    @Override
    public Order add(Integer userId, Integer shippingId) {
        if(shippingService.get(shippingId)==null){
            throw new RuntimeException("收货地址不存在");
        }
        Order order=new Order();
        order.setUserId(userId);
        order.setUpdateTime(new Date());
        order.setShippingId(shippingId);//设置运单id
        order.setCreateTime(new Date());//创建时间
        order.setOrderNo(System.currentTimeMillis());//订单号是当前时间毫秒数
        order.setPaymentType(1);//支付类型，1：在线支付
        order.setPostage(0);//运费，暂时无法设置
        order.setStatus(10);//订单状态:0-已取消-10-未付款，20-已付款，40-已发货，50-交易成功，60-交易关闭
        List<Cart> cartList = cartService.getListByUserId(userId);//获取当前用户的购物车
        BigDecimal payment=new BigDecimal(0);
        for(Cart cart:cartList){//遍历购物车中每条商品
            Product product = productService.get(cart.getProductId());//联查得到对应商品
            cartService.delete(cart.getId());//从购物车中删除该记录
            //获取单条总价（单价*数量）
            BigDecimal price = product.getPrice().multiply(new BigDecimal(cart.getQuantity()));
            payment=payment.add(price);//加到总价
            OrderItem orderItem=new OrderItem();
            orderItem.setCreateTime(new Date());
            orderItem.setUserId(userId);
            orderItem.setCurrentUnitPrice(product.getPrice());
            orderItem.setOrderNo(order.getOrderNo());
            orderItem.setProductId(cart.getProductId());
            orderItem.setProductName(product.getName());
            orderItem.setProductImage(product.getMainImage());
            orderItem.setQuantity(cart.getQuantity());
            orderItem.setTotalPrice(price);
            orderItem.setUpdateTime(new Date());
            orderItemService.add(orderItem);
        }
        order.setPayment(payment);
        return repository.save(order);
    }

    @Override
    public OrderVo toVo1(Order order) {
        OrderVo vo=new OrderVo();
        BeanUtils.copyProperties(order,vo);//将order赋值给orderVo
        //获取所有当前所有相关OrderItem
        List<OrderItem> itemList = orderItemService.getListByOrderNo(order.getOrderNo());
        //将该list变成对应vo的list并赋值给orderVo
        vo.setOrderItemVoList(orderItemService.toVoList(itemList));
        return vo;
    }

    @Override
    public Order getOrderByUserIdAndOrderNo(Integer userId, Long orderNo) {
        Order order=new Order();
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        return repository.findOne(Example.of(order));
    }

    @Override
    public Order getOrderByOrderNo(Long orderNo) {
        Order order=new Order();
        order.setOrderNo(orderNo);
        return repository.findOne(Example.of(order));
    }

    @Override
    public Pages<OrderVo> getPagesByUserId(@NotNull Integer userId, @NotNull Integer pageNum, @NotNull Integer pageSize, String orderBy) {
        Sort sort=this.getOrderBy(orderBy);
        Order o=new Order();
        o.setUserId(userId);
        //获取order list
        Pageable pageable=new PageRequest(pageNum-1,pageSize,sort);
        Page<Order> page = repository.findAll(Example.of(o),pageable);
        List<Order> content = page.getContent();
        //获取总条数
        Integer total= Math.toIntExact(page.getTotalElements());
        //获取OrderVo的List
        List<OrderVo> list=new ArrayList<>();
        for(Order order:content){
            list.add(this.toVo(order));
        }
        return new Pages<>(pageNum,pageSize,orderBy,list,total);
    }

    /**
     * 获得排序
     * @param orderBy
     * @return
     */
    private Sort getOrderBy(String orderBy){
        Sort sort=null;
        //获取排序
        if(orderBy!=null){
            String[] split = orderBy.split("_");
            switch (split[1]){
                case "desc":
                    sort=new Sort(Sort.Direction.DESC,split[0]);
                    break;
                case "asc":
                    sort=new Sort(Sort.Direction.ASC,split[1]);
                    break;
            }
        }
        return sort;
    }
    @Override
    public Pages<OrderVo> getPages(@NotNull Integer pageNum, @NotNull Integer pageSize, String orderBy) {
        Sort sort=this.getOrderBy(orderBy);
        //获取order list
        Pageable pageable=new PageRequest(pageNum-1,pageSize,sort);
        Page<Order> page = repository.findAll(pageable);
        List<Order> content = page.getContent();
        //获取总条数
        Integer total= Math.toIntExact(page.getTotalElements());
        //获取OrderVo的List
        List<OrderVo> list=new ArrayList<>();
        for(Order order:content){
            list.add(this.toVo(order));
        }
        return new Pages<>(pageNum,pageSize,orderBy,list,total);
    }

    @Override
    public Pages<OrderVo> getPagesByOrderNo(Long orderNo, Integer pageNum, Integer pageSize, String orderBy) {
        Sort sort=this.getOrderBy(orderBy);
        Pageable pageable=new PageRequest(pageNum-1,pageSize,sort);
        //模糊查询orderNo
        Page<Order> page = repository.findByOrderNoLike("%" + orderNo.toString() + "%", pageable);
        List<Order> orderList = page.getContent();

        Integer total= Math.toIntExact(page.getTotalElements());

        //获取OrderVo的List
        List<OrderVo> list=new ArrayList<>();
        for(Order order:orderList){
            list.add(this.toVo(order));
        }
        return new Pages<>(pageNum,pageSize,orderBy,list,total);
    }

    @Override
    public OrderVo toVo(Order order) {
        //获取OrderItemVoList
        List<OrderItem> orderItemList = orderItemService.getListByOrderNo(order.getOrderNo());
        OrderVo vo=new OrderVo();
        BeanUtils.copyProperties(order,vo);
        switch (order.getPaymentType()){//设置支付描述
            case 1:vo.setPaymentTypeDesc("在线支付");
                break;
        }
        //设置状态描述
        String statusDesc=null;
        switch (order.getStatus()){
            case 0: statusDesc=OrderStatusEnum.CANCELED.getMsg();break;
            case 10: statusDesc=OrderStatusEnum.UNPAID.getMsg();break;
            case 20: statusDesc=OrderStatusEnum.PAID.getMsg();break;
            case 40: statusDesc=OrderStatusEnum.SHIPPED.getMsg();break;
            case 50: statusDesc= SUCCESS.getMsg();break;
            case 60: statusDesc=OrderStatusEnum.CLOSED.getMsg();break;
        }
        vo.setStatusDesc(statusDesc);
        //设置orderVo的itemVoList
        vo.setOrderItemVoList(orderItemService.toVoList(orderItemList));
        //设置orderVo的shippingVo
        ShippingVo shippingVo = shippingService.getShippingVo(order.getShippingId());
        vo.setShippingVo(shippingVo);
        //设置收货人名
        if(shippingVo!=null){
            vo.setReceiverName(shippingVo.getReceiverName());
        }
        return vo;
    }

    @Transactional
    @Override
    public void cancel(Integer userId, Long orderNo) {
        Order order=new Order();
        order.setUserId(userId);
        order.setOrderNo(orderNo);
        Order one = repository.findOne(Example.of(order));
        if(one==null){
            throw new RuntimeException("没有找到订单");
        }
        if(one.getStatus()>OrderStatusEnum.UNPAID.getCode()){
            throw new RuntimeException("此订单已付款，无法取消");
        }
        one.setStatus(0);
    }

    @Override
    public Result<Map<String,String>> pay(Long orderNo, Integer userId, String path) {
        Order order = this.getOrderByUserIdAndOrderNo(userId,orderNo);
        if(order==null){
            throw new RuntimeException("用户没有该订单");
        }
        Result<Map<String,String>> resultMap=new Result<>();//设置将返回的result
        Map<String,String> map=new HashMap<>();
        resultMap.setData(map);
        map.put("orderNo",orderNo.toString());

        // (必填) 商户网站订单系统中唯一订单号，64个字符以内，只能包含字母、数字、下划线，
        // 需保证商户系统端不能重复，建议通过数据库sequence生成，
        String outTradeNo = orderNo.toString();

        // (必填) 订单标题，粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
        String subject = new StringBuilder("HappyMall扫码支付，订单号：").append(outTradeNo).toString();

        // (必填) 订单总金额，单位为元，不能超过1亿元
        // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
        String totalAmount = order.getPayment().toString();

        // (可选) 订单不可打折金额，可以配合商家平台配置折扣活动，如果酒水不参与打折，则将对应金额填写至此字段
        // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
        String undiscountableAmount = "0";

        // 卖家支付宝账号ID，用于支持一个签约账号下支持打款到不同的收款账号，(打款到sellerId对应的支付宝账号)
        // 如果该字段为空，则默认为与支付宝签约的商户的PID，也就是appid对应的PID
        String sellerId = "";

        // 订单描述，可以对交易或商品进行一个详细地描述，比如填写"购买商品2件共15.00元"
        String body = new StringBuilder("订单").append(outTradeNo).append("购买商品共").append(totalAmount).append("元").toString();

        // 商户操作员编号，添加此参数可以为商户操作员做销售统计
        String operatorId = "test_operator_id";

        // (必填) 商户门店编号，通过门店号和商家后台可以配置精准到门店的折扣信息，详询支付宝技术支持
        String storeId = "test_store_id";

        // 业务扩展参数，目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法)，详情请咨询支付宝技术支持
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId("2088100200300400500");

        // 支付超时，定义为120分钟
        String timeoutExpress = "120m";

        // 商品明细列表，需填写购买商品详细信息，
        List<GoodsDetail> goodsDetailList = new ArrayList<>();
        List<OrderItem> itemList = orderItemService.getListByOrderNo(orderNo);
        for(OrderItem item:itemList){
            // 创建一个商品信息，参数含义分别为商品id（使用国标）、名称、单价（单位为分）、数量，如果需要添加商品类别，详见GoodsDetail
            GoodsDetail detail= GoodsDetail.newInstance(item.getProductId().toString(),item.getProductName(),
                    item.getCurrentUnitPrice().multiply(BigDecimal.valueOf(100)).longValue(),item.getQuantity());
            goodsDetailList.add(detail);
        }

        // 创建扫码支付请求builder，设置请求参数
        AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
                .setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo)
                .setUndiscountableAmount(undiscountableAmount).setSellerId(sellerId).setBody(body)
                .setOperatorId(operatorId).setStoreId(storeId).setExtendParams(extendParams)
                .setTimeoutExpress(timeoutExpress)
                .setNotifyUrl(returnUrl+"/order/alipay_callback.do")//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
                .setGoodsDetailList(goodsDetailList);

        /*
         *  一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数
         *  Configs会读取classpath下的zfbinfo.properties文件配置信息，如果找不到该文件则确认该文件是否在classpath目录
         */

        Configs.init("zfbinfo.properties");

        /* 使用Configs提供的默认参数
         *  AlipayTradeService可以使用单例或者为静态成员对象，不需要反复new
         */

        AlipayTradeService tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);

        switch (result.getTradeStatus()) {
            case SUCCESS:
                LOGGER.info("支付宝预下单成功: )");

                AlipayTradePrecreateResponse response = result.getResponse();
                dumpResponse(response);

                // 修改为运行机器上的路径
                String basePath=qrUrl;
                String filePath=String.format("/qr-%s.png",response.getOutTradeNo());
                String qrPath = basePath+filePath;
                ZxingUtils.getQRCodeImge(response.getQrCode(),256,qrPath);
                Taest taest =new Taest();
                taest.upload(qrPath,filePath);
                LOGGER.info("qrPath:" + qrPath);
                map.put("qrUrl",qiniuUrl+"/"+filePath);
                resultMap.setStatus(0);
                return resultMap;

            case FAILED:
                LOGGER.error("支付宝预下单失败!!!");
                throw new RuntimeException("支付宝预下单失败!!!");
            case UNKNOWN:
                LOGGER.error("系统异常，预下单状态未知!!!");
                throw new RuntimeException("系统异常，预下单状态未知!!!");

            default:
                LOGGER.error("不支持的交易状态，交易返回异常!!!");
                throw new RuntimeException("不支持的交易状态，交易返回异常!!!");
        }

    }

    /**
     * 简单打印应答
     * @param response
     */
    private void dumpResponse(AlipayResponse response) {
        if (response != null) {
            LOGGER.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));
            if (StringUtils.isNotEmpty(response.getSubCode())) {
                LOGGER.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(),
                        response.getSubMsg()));
            }
            LOGGER.info("body:" + response.getBody());
        }
    }
    @Override
    public Boolean aliCallback(Map<String,String>params){
        Long orderNo = Long.valueOf(params.get("out_trade_no"));
        String tradeStatus = params.get("trade_status");
        String tradeNo = params.get("trade_no");

        Order order = this.getOrderByOrderNo(orderNo);
        if(order==null){
            throw new RuntimeException("无效订单，忽略");
        }
        if(order.getStatus() != OrderStatusEnum.UNPAID.getCode()){
            throw new RuntimeException("订单已支付，支付宝重复调用");
        }
        if(AliCallbackEnum.TRADE_SUCCESS.getMsg().equals(tradeStatus)){
            try {
                order.setPaymentTime(DateUtils.parseDate(params.get("gmt_payment"),
                        new String[]{"yyyy-MM-dd HH:mm:ss"}));
            } catch (ParseException e) {
                throw new RuntimeException("日期格式转换错误");
            }
            order.setStatus(OrderStatusEnum.PAID.getCode());
            this.update(order);
        }
        PayInfo payInfo=new PayInfo();
        payInfo.setUserId(order.getUserId());
        payInfo.setCreateTime(new Date());
        payInfo.setOrderNo(orderNo);
        payInfo.setPayPlatform(1);
        payInfo.setPlatformStatus(tradeStatus);
        payInfo.setPlatformNumber(tradeNo);
        payInfoService.add(payInfo);
        return true;
    }

    @Override
    public void update(Order order){
        if(repository.exists(order.getId())){
            repository.save(order);
        }else{
            throw new RuntimeException("订单不存在");
        }
    }
}
